package io.kiki.stack.http.feign;

import io.kiki.stack.http.feign.Logger.Level;
import io.kiki.stack.http.feign.codec.Decoder;
import io.kiki.stack.http.feign.codec.ErrorDecoder;

import java.io.IOException;
import java.lang.reflect.Type;

import static io.kiki.stack.http.feign.FeignException.errorReading;
import static io.kiki.stack.http.feign.Util.ensureClosed;

/**
 * The response handler that is used to provide synchronous support on top of standard response
 * handling
 */
public class ResponseHandler {

    private final Level logLevel;
    private final Logger logger;

    private final Decoder decoder;
    private final ErrorDecoder errorDecoder;
    private final boolean dismiss404;
    private final boolean closeAfterDecode;

    private final boolean decodeVoid;

    private final ResponseInterceptorChain executionResponseInterceptorChain;

    public ResponseHandler(Level logLevel, Logger logger, Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404, boolean closeAfterDecode, boolean decodeVoid, ResponseInterceptorChain executionResponseInterceptorChain) {
        super();
        this.logLevel = logLevel;
        this.logger = logger;
        this.decoder = decoder;
        this.errorDecoder = errorDecoder;
        this.dismiss404 = dismiss404;
        this.closeAfterDecode = closeAfterDecode;
        this.decodeVoid = decodeVoid;
        this.executionResponseInterceptorChain = executionResponseInterceptorChain;
    }

    public Object handleResponse(String configKey, Response response, Type returnType, long elapsedTime) throws Exception {
        try {
            response = logAndRebufferResponseIfNeeded(configKey, response, elapsedTime);
            return executionResponseInterceptorChain.next(new InvocationContext(configKey, decoder, errorDecoder, dismiss404, closeAfterDecode, decodeVoid, response, returnType));
        } catch (final IOException e) {
            if (logLevel != Level.NONE) {
                logger.logIOException(configKey, logLevel, e, elapsedTime);
            }
            throw errorReading(response.request(), response, e);
        } catch (Exception e) {
            ensureClosed(response.body());
            throw e;
        }
    }

    private Response logAndRebufferResponseIfNeeded(String configKey, Response response, long elapsedTime) throws IOException {
        if (logLevel == Level.NONE) {
            return response;
        }

        return logger.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
    }
}
